#include "Serial.h"

#define SERIAL_DEBUG_OUTPUT

Serial* Serial::m_instance = nullptr;

Serial::Serial()
{
    TransmitDataReady = 0;
    m_SerialPortDevice = "/dev/ttyArduino";
}

Serial* Serial::Get()
{
    static std::once_flag once;
    std::call_once(once, [&](){ m_instance = new Serial; });
    return m_instance;
}

void Serial::SerialTask()
{
    int fd = -1;
    int err = 0;
    Serial* serial = Get();
    bool serialErrFlag = 1;
    int errcount;
    
    //主循环
    while(1)
    {
        //如果有异常就重新打开串口并清除标志位
        if(serialErrFlag)
        {
            serial->OpenSerialPort(fd);
            serialErrFlag = 0;
        }

        if(fd == -1)
            continue;

        //发送
        if(serial->TransmitDataReady)
        {
            SendStruct data2send;
            serial->transmitBuffer >> data2send;
            bool sendstate = UART0_Send(fd,((char*)(&data2send)),sizeof(SendStruct));
            if(!sendstate)
                cout << "Arduino Send Err\n";
            serial->TransmitDataReady = 0;
        }

        //接收
        if(serial->takeoff)
            continue;

        /*
        //先收一个帧尾
        while(1)
        {
            if(serialErrFlag)
                break;
            char singlebuffer = 0;
            bool received = UART0_Recv(fd,&singlebuffer,1);
            if(!received)
                errcount += 1;
            if(errcount == 10)
            {
                errcount = 0;
                serialErrFlag = 1;
                break;
            }
            if(singlebuffer == serial->receiveFrameEnd)
                break;
        }

        if(serialErrFlag)
            continue;
        */
       
        //接下来试着收一个完整数据帧
        int receivecount = 0;
        char receiveBuffer[4096];
        bool completeflag = 0;
        while(1)
        {
            int recv = serial->UART0_Recv(fd, receiveBuffer + receivecount, 4096 - receivecount);
            receivecount += recv;
            if(receivecount == 1024)
            {
                serialErrFlag = 1;
                break;
            }
            
            if(errcount == 100)
            {
                errcount = 0;
                serialErrFlag = 1;
                break;
            }
            if(recv == 0)  
            {
                errcount += 1;
                continue;
            }
            if(receivecount < sizeof(ReceiveStruct))
                continue;
            for(int i = 0;i < receivecount - sizeof(ReceiveStruct); i++)
            {
                bool frameHeadCorrect = receiveBuffer[i] == serial->receiveFrameHead;
                bool frameEndCorrect = receiveBuffer[i + sizeof(ReceiveStruct) - 1] == serial->receiveFrameEnd;
                if( frameHeadCorrect && frameEndCorrect )
                {
                    ReceiveStruct* data = new ReceiveStruct;
                    memcpy(data,receiveBuffer + i,sizeof(ReceiveStruct));
                    serial->receiveBuffer << *data;
                    completeflag = 1;
                    break;
                }
            }
            if(completeflag)
                break;
        }
    }
}

void Serial::OpenSerialPort(int & _fd)
{
    int err;
    if(_fd != -1)
        Get()->UART0_Close(_fd);
    _fd = -1;
    _fd = open(Get()->m_SerialPortDevice.c_str(),O_RDWR | O_NOCTTY | O_NDELAY);
    do
    {
        err = Get()->UART0_Init(_fd,115200,0,8,1,'N');
    }
    while(false == err || false == _fd);
}

void Serial::SendData(uint8_t cmd,uint16_t datax,uint16_t datay)
{
    SendStruct buffer;
    buffer.frameHead = sendFrameHead;
    buffer.cmd = cmd;
    buffer.dataX = datax;
    buffer.dataY = datay;
    buffer.frameEnd = sendFrameEnd;
    transmitBuffer << buffer;
    TransmitDataReady = 1;
}


ReceiveStruct Serial::GetData()
{
    ReceiveStruct buffer;
    receiveBuffer >> buffer;
    return buffer;
}


int Serial::UART0_Open(int fd, char *port)
{
    int FALSE;
    fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);
    if (fd<0)
    {
        perror("Can't Open Serial Port");
        return(FALSE);
    }
    //恢复串口为阻塞状态
    if(fcntl(fd, F_SETFL, 0) < 0)
    {
        //printf("fcntl failed!\n");
        return(0);
    }
    else
    {
        //printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));
    }
    //测试是否为终端设备
    if(0 == isatty(STDIN_FILENO))
    {
        //printf("standard input is not a terminal device\n");
        return(FALSE);
    }
    else
    {
        //printf("isatty success!\n");
    }
    //printf("fd->open=%d\n",fd);
    return fd;
}

void Serial::UART0_Close(int fd)
{
    close(fd);
}

int Serial::UART0_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)
{

    int   i;
    int   status;
    int   speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};
    int   name_arr[] = {115200,  19200,  9600,  4800,  2400,  1200,  300};

    struct termios options;

    /*  tcgetattr(fd,&options)得到与fd指向对象的相关参数，并将它们保存于options,该函数还可以测试配置是否正确，
        该串口是否可用等。若调用成功，函数返回值为0，若调用失败，函数返回值为1.  */
    if( tcgetattr( fd,&options)  !=  0)
    {
        perror("SetupSerial 1");
        return(0);
    }

    //设置串口输入波特率和输出波特率
    for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
    {
        if  (speed == name_arr[i])
        {
            cfsetispeed(&options, speed_arr[i]);
            cfsetospeed(&options, speed_arr[i]);
        }
    }

    //修改控制模式，保证程序不会占用串口
    options.c_cflag |= CLOCAL;
    //修改控制模式，使得能够从串口中读取输入数据
    options.c_cflag |= CREAD;

    //设置数据流控制
    switch(flow_ctrl)
    {

        case 0 ://不使用流控制
              options.c_cflag &= ~CRTSCTS;
              break;

        case 1 ://使用硬件流控制
              options.c_cflag |= CRTSCTS;
              break;
        case 2 ://使用软件流控制
              options.c_cflag |= IXON | IXOFF | IXANY;
              break;
    }
    //设置数据位
    //屏蔽其他标志位
    options.c_cflag &= ~CSIZE;
    switch (databits)
    {
        case 5    :
                     options.c_cflag |= CS5;
                     break;
        case 6    :
                     options.c_cflag |= CS6;
                     break;
        case 7    :
                 options.c_cflag |= CS7;
                 break;
        case 8:
                 options.c_cflag |= CS8;
                 break;
        default:
                 //fprintf(stderr,"Unsupported data size\n");
                 return (0);
    }
    //设置校验位
    switch (parity)
    {
        case 'n':
        case 'N': //无奇偶校验位。
                 options.c_cflag &= ~PARENB;
                 options.c_iflag &= ~INPCK;
                 break;
        case 'o':
        case 'O'://设置为奇校验
                 options.c_cflag |= (PARODD | PARENB);
                 options.c_iflag |= INPCK;
                 break;
        case 'e':
        case 'E'://设置为偶校验
                 options.c_cflag |= PARENB;
                 options.c_cflag &= ~PARODD;
                 options.c_iflag |= INPCK;
                 break;
        case 's':
        case 'S': //设置为空格
                 options.c_cflag &= ~PARENB;
                 options.c_cflag &= ~CSTOPB;
                 break;
        default:
                 //fprintf(stderr,"Unsupported parity\n");
                 return (0);
    }
    // 设置停止位
    switch (stopbits)
    {
        case 1:
                 options.c_cflag &= ~CSTOPB; break;
        case 2:
                 options.c_cflag |= CSTOPB; break;
        default:
                       //fprintf(stderr,"Unsupported stop bits\n");
                       return (0);
    }

    //修改输出模式，原始数据输出
    options.c_oflag &= ~OPOST;

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    //options.c_lflag &= ~(ISIG | ICANON);

    //设置等待时间和最小接收字符
    options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */
    options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */

    //如果发生数据溢出，接收数据，但是不再读取 刷新收到的数据但是不读
    tcflush(fd,TCIFLUSH);

    //激活配置 (将修改后的termios数据设置到串口中）
    if (tcsetattr(fd,TCSANOW,&options) != 0)
    {
        perror("com set error!\n");
        return (0);
    }
    return (1);
}

int Serial::UART0_Init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity)
{
    int err=0;
    //设置串口数据帧格式
    if (UART0_Set(fd,115200,0,8,1,'N') == 0)
    {
        return 0;
    }
    else
    {
        return  1;
    }
}

int Serial::UART0_Recv(int fd, char *rcv_buf,int data_len)
{
    int len,fs_sel;
    fd_set fs_read;

    struct timeval time;

    FD_ZERO(&fs_read);
    FD_SET(fd,&fs_read);

    time.tv_sec = 1;
    time.tv_usec = 0;

    //串口的多路通信
    fs_sel = select(fd + 1,&fs_read,NULL,NULL,&time);
    //printf("fs_sel = %d\n",fs_sel);
    if(fs_sel)
    {
        len = read(fd,rcv_buf,data_len);
        return len;
    }
    else
    {
        return 0;
    }
}


int Serial::UART0_Send(int fd, char *send_buf,int data_len)
{
    int len = 0;

    len = write(fd,send_buf,data_len);
    if (len == data_len )
    {
        //("send data is");
        for(int i = 0;i<data_len;i++)
        {
            //printf("%d ",(int)(*(send_buf + i)));
        }
        //printf("\n");
        return len;
    }
    else
    {

        tcflush(fd,TCOFLUSH);
        return 0;
    }

}

void Serial::SetTakeOffFlag(bool _takeoff)
{
    Get()->takeoff = _takeoff;
}